# -*- coding: utf-8 -*-
#copyright 2009 Kevin Daley
#This file is a part of Cerebra
#Cerebra is free software: you can redistribute it and/or modify
 #   it under the terms of the GNU General Public License as published by
  #  the Free Software Foundation, either version 3 of the License, or
   # (at your option) any later version.

   # This program is distributed in the hope that it will be useful,
   # but WITHOUT ANY WARRANTY; without even the implied warranty of
   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   # GNU General Public License for more details.

   # You should have received a copy of the GNU General Public License
   # along with this program.  If not, see <http://www.gnu.org/licenses/>.

#Kevin Daley, Lead Developer.  still.horse@gmail.com.

import stackless, glob, re, sys, random, numpy, numpy.random, os, pickle, string, socket, urllib
import stacklesssocket
stacklesssocket.install();
import socket
from string import *
from math import *
from ctypes import *
from pyparsing import *
class node(object):
	values=[]
	charset=[[["="]],[["[","]"],["{", "}"]],[["(",")"],["'","'"],["-"]],[["\\"],["#"],["%"],["."]],[["v"], ["s"], ["|"]]]
	objs=[[],[],[],[],[]]
	def GPUroute(self):
		stackless.schedule()
		self.s =socket.socket(socket.AF_TIPC, socket.SOCK_STREAM,0)
		s=self.s
		stackless.schedule()
		s.sendto("Watson I need you",(socket.TIPC_ADDR_ID, self.server, self.port,0))
		self.port=int(s.recvfrom(1024)[0])
	def __init__(self,  dict=None, *args, **kwds):
		print " setting up node attributes..."
		self.queue={}
		for name in kwds.keys():
			self.__setattr__(name, kwds[name])
		if dict!=None:
			for name in dict.keys():	
				self.__setattr__(name, dict[name])
				kwds[name]=dict[name]
		if(not kwds.has_key("pgm")):
			self.pgm=[]
		print
		print "loading data and spectrum elements..."
			
		stackless.tasklet(self.GPUroute())()
		self.s.sendto("init") 
		self.s.sendto(string.join(self.input.astype(str).tolist()," "))
		self.s.sendto(string.join((str(i) for i in self.spectrum), " "),(socket.TIPC_ADDR_ID, self.server, self.port,0))
		self.s.sendto(str(self.unit),(socket.TIPC_ADDR_ID, self.server, self.port,0))
		self.s.sendto(str(self.timestep),(socket.TIPC_ADDR_ID, self.server, self.port,0))
		self.s.sendto(str(self.ID),(socket.TIPC_ADDR_ID, self.server, self.port,0))
		print "..."
		print 
		print "parsing optional node attributes"
		if(not kwds.has_key("vtable")):
			self.vtable={}
		if(not kwds.has_key("Naccess")):
			self.Naccess=1
		self.counter=0
		self.filename=self.filename if kwds.has_key("filename" ) else str(random.random())
		self.src=string.split(file.read().split(" ;; ")) if kwds.has_key("filename") else []
		print "done."
		print "creating __call__ tasklet"
		if self.__dict__.has_key("astring"):
			pass
		else:
			self.astring=None
		
		self.wakeup=1
		print "node %i initialized"%(self.ID)
		self.paused=0
		self.interject=0
	def unpause(self, mode=0):
		if(mode==0):
			self.paused=0
		elif mode==1:
			self.interject=0
		while(len(self.queue.keys())<0):
				pass
		
	def pause(self, mode=0):
		if(mode==0):
			self.paused=1
		elif mode==1:
			self.interject=1
		while(len(self.queue.keys())>0):
				pass
	def program(self):
		stackless.schedule()
		while(1):
			if not self.paused:
				stackless.schedule()
				if(self.astring !=None and ";" in self.astring):
					self.queue["program"]=1
					print "%i node: lamda-interpret source verbiage: '%s'"%(self.ID, self.astring)
					print "..."
					self.astring=self.astring.replace(";","")
					self.src.append("lambda x,y,z:%s"%self.interpretpgm())
					self.s.sendto("lambda x,y,z:%s"%self.interpretpgm(), (socket.TIPC_ADDR_ID, self.server, self.port,0))
					del self.queue["program"]
					self.astring=None
					print "..done"
	def autobalance(self):
		stackless.schedule()
		while(1):
			if not self.paused:
				stackless.schedule()
				self.queue["autobalance"]=1
				print "%i node: execute autobalancing procedure..."%self.ID
				if(self.parent!= None):
					self.tmp=self.parent.ID
					tmp=self.tmp
					ix=len(self.parents.keys())
					if(self.Naccess % 8 and ix>1):
						self.tmp=self.parent.Naccess
						err=abs(self.tmp-self.Naccess/self.tmp*len(self.neighbors))
						for im in xrange(log(len(self.parents))):
							i=random.randint(0,len(self.parents)-1)
							self.tmp2=self.parents[i].ID
							tmp=self.tmp2
							if(self.tmp2!=self.tmp):
								self.tmp2=self.parents[self.parents.keys()[i]].Naccess
								self.tmp3=self.parents[self.parents.keys()[i]].cstrings
								if(int(abs(self.tmp2-self.Naccess/self.tmp2*len(self.tmp3)))>=err):
									tmp=self.tmp
						if(ix!=len(self.parents.keys())):
							self.attach_to_parent(self.parents[abs(self.ID-tmp)])
							print "%i node: migrated to surrogate parent %i"%(self.ID, tmp)
				if self.children!=None:
					for k in self.children.keys():
						self.child=self.children[k]
						i=self.child
				self.counter+=1
				print "..done."
				del self.queue["autobalance"]
	def run(self):
		stackless.schedule()
		while(1):
			stackless.schedule()
			if self.astring!=None and not (";" in self.astring) and not self.paused:
				self.queue["run"]=1
				print "%i node: use arguments (%s)" %(self.ID, self.astring)
				args=(i for i in self.astring.split(","))
				del self.queue["run"]
				self.s.sendto(args, (socket.TIPC_ADDR_ID, self.server, self.port,0))
				self.estring=self.vtable[0]
				self.astring=self.s.recvfrom(65565*2)[0]
				print "...done."
				print 
				self.Naccess+=1
	def append_child(self,child, s):
		self.children[int(abs(child.ID-self.ID))]=child
		self.cstrings.append(str(s))
		child.parents[int(abs(child.ID-self.ID))]=self
		self.estring=join(self.cstrings)
	def attach_to_parent(self, parent):
		parent.append_child(self, self.estring)
	def change_string(self,string_new):
		self.cstring=string_new
		for i in self.parents:
			i.cstrings[int(abs(i.ID-self.ID))]=string_new
	def parsestr(self, astring, ix=0):
		print "..parsing... "
		punctuation=[]
		alist=[]
		digits = "0123456789*"
		number = Word( digits )
		try:
			for i in self.charset[len(self.charset)-(ix+1)]:
					if len(i)==1:
						punctuation.append(Optional(i[0])+number)
					else:
						Optional(i[0])+ number+ Optional(i[1])
			grammar=And( punctuation )
			def replace(seq):
				for data in seq:
					try:
						astring.replace(data,"*")
						alist.append(data)
					except TypeError:
						if isinstance(data,int):
							astring.replace(str(data),"*")
							alist.append(str(data))
						else:
							replace(data)
			replace(grammar.scanString(astring))
			self.objs.append(alist)
			self.parsestr(astring, ix+1)
		except IndexError:
			return 
	def op(self,x,opstr):
		if opstr== "#":
				return "int(self.network.sustantiv(c_int("+x+")))"
		elif opstr==".":
				return "int(self.network.instantiv(c_int("+x+")))"
		elif opstr=="%":
				return "int(self.network.descriptiv(c_int("+x+")))"
		elif opstr== "\\":
				return "int(self.network.set(c_int("+x+")))"
	def op2(self, x, y, opstr):
		if opstr=="-":
				return "int(self.network.equiv(c_int("+x+"), c_int("+y+")))"
		elif opstr=="=":
				return "int(self.network.assign(c_int("+x+"),c_int("+y+")))"
	def interpretpgm(self, i=0):
		val=[]
		hold=0
		astring=self.astring
		if(i==0):
			self.parsestr(astring)
		if i<5:
			print "whittling down.."
			if(i+1<5):
				self.values =self.interpretpgm(i+1)
			for j in self.objs[i]:
				if(j=="*"):
					val.append(self.values.pop())
				elif(j in ["\\",".","#","%"]):
					opstr=j
					val.append(self.op(val.pop(),j))
				if(hold==1):
					val.append(self.op2(val.pop(),val.pop(),j))
					hold=0
				elif(j in ["-", '=']):
					opstr=j
					hold=1
				print "..."
				return val.pop()
	def __getitem__(self,key):
		return self.vtable[key]
	def __setitem__(self,key,item):
		self.vtable[key]=item
	def __delitem__(self,key):
		del self.vtable[key]
	def __mod__(self,x):
		return self.interpretpgm(x)
	def __iadd__(self,x):
		if len(self.estring.split(x.estring))!=0:
			self.append_child(x, x.estring)
			return self
		else:
			for i in cstrings:
				if len(i.split(x.estring))!=0:
					return i.__iadd__(x)
		return x
	def __imod__(self,x):
		self.change_string(x)
	def attach_out(channel):
		self.channel=channel
	def dump(self):
		print repr(self)
		self.s.sendto(("recv", self.ID),(socket.TIPC_ADDR_ID, self.server, self.port,0))
		self.output=numpy.ndarray(self.s.recvfrom(1024*1024*64*4+1024*1024+1024*4)[0].split(' ').as_type(int))
		self.bnd=self.output
		f=file("output.cache", "rw+")
		for i in xrange(1024):
			for j in xrange(1024):
				k=0
				for m in xrange(64):
					m+=self.output[1024*1024*k+1024*j+i]
				f.append("%i %i %f \n"%(i,j,k))
		f.close()
		print join(self.src," ;; ")
		return self.__dict__
	def __setattr__(self, name, value):
		self.__dict__[name]=value
			
			

